﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections.ObjectModel;
using System.Xml;
using System.Xml.Linq;
using System.ComponentModel;

namespace MassIntellect.WorkIt
{
	public class WorkGroup : WorkFrame
	{
		public WorkGroup()
			: base()
		{
			MinimumActivatedFrames = 1;
		}

		private Dictionary<string, IWorkFrame> wfs = new Dictionary<string, IWorkFrame>();
		private Dictionary<string, CrossPipe> sws = new Dictionary<string, CrossPipe>();

		[Serialize]
		[Description("Пошаговая активация. Необходима для дифференцированной активации фреймов. Полезно для отлова багов и упрощения логирования.")]
		public bool IsStepActivate { get; set; }

		[Serialize]
		[Description("Минимально необходимое количество активированных фреймов для активации данной группы.")]
		public int MinimumActivatedFrames { get; set; }

		public int ActivityCount
		{
			get { return sws.Values.Count(s => s.IsActive) + Frames.Count(s => s.IsEnabled && s.IsActive); }
		}

		public override bool IsActive
		{
			get
			{
				return sws.Values.Any(s => s.IsActive)
					|| (State == State.InWork && Frames.Sum(s => (s.IsEnabled && s.IsActive) ? 1 : 0) >= MinimumActivatedFrames);
			}
			set
			{
				base.IsActive = value;
			}
		}

		protected override void protectedPulse(PulseArgs args)
		{
			foreach (var item in sws.Values.Where(s => s.IsActive).Reverse())
				item.Activate();
			if (State == State.InWork)
				foreach (var item in Frames
						.Where(s => s.IsEnabled && s.IsActive).Reverse())
				{
					item.Activate();
#if DEBUG
					//Console.ReadLine(); 
#endif
				}
		}

		public IEnumerable<IWorkFrame> Frames
		{
			get
			{
				return wfs.Values;
			}
		}

		public override XElement Serialize()
		{
			XElement xe = base.Serialize();
			if (wfs.Count > 0)
				xe.Add(new XElement("Frames",
						wfs.Values.Select(s => s.Serialize()))
					);
			if (sws.Count > 0)
				xe.Add(new XElement("Switch",
					sws.Values.Select(s => XmlSerializeProvider.Serialize(s, false, null, null))));
			xe.Add(new XElement("Services", innerSL.Serialize()));
			return xe;
		}

		public override IEnumerable<IPipe> Pipes
		{
			get
			{
				return wfs.Values
					.Where(s => s.Scope == Scope.Public)
					.SelectMany(s => s.Pipes
						.Where(s1 => s1.Scope == Scope.Public)
						.Select(s1 => s1.GetInterface<IPipe>()));
			}
		}

		private void BuildService(XElement xe)
		{
			foreach (var item in xe.Elements("Services").Elements())
				innerSL.SetUp(item);
		}

		private void BuildFrames(XElement xe)
		{
			foreach (var item in xe.Elements("Frames").Elements()
									  .Select(s => Factory.CreateFrame(s)))
			{
				item.OnEcho += EchoInConsole.OnEcho;
				item.OnLog += EchoInConsole.OnEcho;
				AddServiceLocatorDependedItem(item as IServiceLocatorDepend);
				wfs.Add(item.Name, item);
			}
		}

		private void AddServiceLocatorDependedItem(IServiceLocatorDepend sld)
		{
			if (sld != null)
				sld.ServiceLocator = innerSL;
		}

		private void BuildSwitch(XElement xe)
		{
			CrossPipe cp;
			foreach (var item in xe.Elements("Switch").Elements())
			{
				cp = IsStepActivate
					? new TurnCrossPipe()
					: new CrossPipe();
				XmlSerializeProvider.SetUp(cp, item);
				AddSwitch(cp, true);
			}
		}

		private void AddSwitch(CrossPipe sw, bool autoConnect)
		{
			if ((!sws.ContainsKey(sw.Mnemo)).IfFalse(() => new Exception("Канал {0} уже создан.".goFormat(sw.Mnemo)))
				&& wfs.ContainsKey(sw.outName).IfFalse(() => new Exception("Не найден говорящий кадр {0}".goFormat(sw.outName)))
				&& wfs.ContainsKey(sw.inName).IfFalse(() => new Exception("Не найден слушающий кадр {0}".goFormat(sw.inName)))
				&& ((sw.outPipe.IsEmpty() && (sw.OutPipe = wfs[sw.outName].Pipes.FirstOrDefault()) != null) || (sw.OutPipe = wfs[sw.outName][sw.outPipe]) != null)
					.IfFalse(() => new Exception("Не найден канал {0} слушающего кадра {1}".goFormat(sw.inPipe, sw.inName)))
				&& ((sw.inPipe.IsEmpty() && (sw.InPipe = wfs[sw.inName].Pipes.FirstOrDefault()) != null) || (sw.InPipe = wfs[sw.inName][sw.inPipe]) != null)
					.IfFalse(() => new Exception("Не найден канал {0} говорящего кадра {1}".goFormat(sw.outPipe, sw.outName))))
			{
				if (autoConnect)
					sw.Connect();
				this.sws.Add(sw.Mnemo, sw);
			}
		}



		public override void SetUp(XElement xe)
		{
			//try
			//{
			base.SetUp(xe);
			BuildFrames(xe);
			BuildSwitch(xe);
			BuildService(xe);
			//}
			//catch (Exception ex)
			//{
			//    ThrowException(ex);
			//}
		}

		private ServiceLocator innerSL = new ServiceLocator();
	}
}
